App.require(["canvasTool"], function (canvasTool) {
    function HelpAnimation(parent) {
        this.parent = parent;
        this.animations = [];
        this._animations = [];
        this.animationObj = {};
        this.animationList = [];
        this.isAnimate = false;
        this.notStart = false;
        this.isInited = false;
    }

    HelpAnimation.prototype = {
        constructor: HelpAnimation,
        destroy: function () {
            this.parent = null;
            this.animations.length = 0;
            this.animations = null;
            this._animations.length = 0;
            this._animations = null;
            this.animationObj = null;
            this.animationList.length = 0;
            this.animationList = null;
        },
        addPropertyAnimation: function (str, target) {
            if (this.isInited) {
                this._addPropertyAnimation(str, target);
            }
            else {
                this._animations.push({ str: str, target: target });
            }
        },
        init: function () {
            this.isInited = true;
            var _animations = this._animations;
            if (_animations.length > 0) {
                for (var i = _animations.length - 1; i >= 0; i--) {
                    var _animation = _animations[i];
                    this._addPropertyAnimation(_animation.str, _animation.target);
                    _animations.splice(i, 1);
                }
            }
            return false;
        },
        addProperty: function (obj) {
            this.animationList.push(obj);
            var that = this;
            if (obj.type === "animation") {
                obj.startTime = Date.now();
                if (!this.isAnimate) {
                    this.isAnimate = true;
                    var animation = new Animation(10, 0, Animation.normal);
                    this.update(this, 0, Date.now());
                    animation.onUpdate(function (time) {
                        that.update(this, time, Date.now());
                    });
                    this.parent.addAnimation(animation);
                }
            }
            else if (!this.isAnimate && !this.notStart) {
                this.notStart = true;
                requestAnimationFrame(function () {
                    that.update(null, null, Date.now(), true);
                    that.notStart = false;
                    that.isAnimate = false;
                })
            }
        },
        addPropertyAnimationList: function () {
            var animationObj = this.animationObj;
            for (var key in animationObj) {
                var list = animationObj[key];
                for (var i = 0; i < list.length; i++) {
                    if (list[i].isAnimate) {
                        list[i].isAnimate = false;
                        var objList = this.createAnimationOptions(list[i], key);
                        if (objList) {
                            for (var j = 0; j < objList.length; j++) this.addProperty(objList[j]);
                        }
                    }
                }
            }
        },
        isHas: function (id) {
            var list = this.animations;
            for (var i = 0; i < list.length; i++) {
                if (list[i].id === id) return list[i];
            }
            return false;
        },
        createAnimationOptions: function (obj, key) {
            var animation = this.isHas(key);
            if (animation) {
                var keyframes = animation.keyframes;
                var result = [], target = obj.target.property, oldObj = {}, currentObj = {};
                for (var i = 0; i < keyframes.length; i++) {
                    var newObj = keyframes[i], _oldObj = {}, _currentObj = {};
                    for (var key in newObj) {
                        if (key != "keyframetime" && key != "fn") {
                            _oldObj[key] = oldObj[key] = currentObj[key] || oldObj[key] || target[key];
                            _currentObj[key] = currentObj[key] = newObj[key];
                            if (typeof _oldObj[key] === "undefined") _oldObj[key] = canvasTool.defaultKey[key];
                            if (typeof _currentObj[key] === "undefined")  _currentObj[key] = canvasTool.defaultKey[key];
                        }
                    }
                    var durationObj = canvasTool.createDurationObj(_oldObj, _currentObj, 0);
                    result.push({
                        type: "animation",
                        target: target,
                        animationObj:durationObj,
                        old: _oldObj,
                        current: _currentObj,
                        duration: obj.animation.duration * (newObj.keyframetime - (i == 0 ? 0 : keyframes[i - 1].keyframetime)),
                        delay: obj.animation.duration * (i == 0 ? 0 : keyframes[i - 1].keyframetime) + obj.animation.delay,
                        animationType: newObj.fn || "ease",
                        time: 1,
                        component: obj.target,
                        startEvent: i == 0 ? "animationstart": "",
                        emitEvent: i === keyframes.length - 1 ? "animationend": ""
                    });
                }
                return result;
            }
            return null;
        },
        update: function (animation, elapsed, now, isPre) {
            var parent = this.parent;
            this.addPropertyAnimationList();
            if (!isPre) parent.draw();
            var animationList = this.animationList, removeList = [];
            for (var i = 0; i < animationList.length; i++) {
                var aObj = animationList[i];
                if (aObj.type === "instance") {
                    if (aObj.current) {
                        for (var key in aObj.current) {
                            aObj.target[key] = aObj.current[key];
                        }
                    }
                    removeList.push(i);
                }
                else if (aObj.type === "animation") {
                    if (!aObj.component.parent) {
                        removeList.push(i);
                        continue;
                    }
                    var elap = now - aObj.startTime;
                    if (elap - this.computeAnimationTotal(aObj) >= 0) {
                        removeList.push(i);
                        for (var key in aObj.current) {
                            aObj.target[key] = aObj.current[key];
                        }
                    }
                    else {
                        var elapseObj = this.createElapseObj(aObj, elap);
                        if (!elapseObj) continue;
                        if (!aObj.isStart) {
                            aObj.isStart = true;
                            aObj.component._duration = aObj.animationObj;
                            if (aObj.startEvent) aObj.component.dispatchCustomEvent(aObj.startEvent, { origin: aObj });
                        }
                        aObj.component.data.isChange = true;
                        if (typeof aObj.component.resetOption === "function") aObj.component.resetOption();
                    }
                }
            }
            if (isPre) parent.draw();
            for (var i = removeList.length - 1; i >= 0; i--) {
                var index = removeList[i], animation = animationList[index];
                if (animation.type == "animation") {
                    var isNext = this._resetSameTargetAnimation(animation);
                    if (!isNext) {
                        var currentObj = animation.current, target = animation.component;
                        for (var key in currentObj) {
                            target[key] = currentObj[key];
                        }
                        delete animation.component._duration;
                    }
                }
                if (animation.emitEvent) {
                    animation.component.dispatchCustomEvent(animation.emitEvent, {});
                }
                animationList.splice(index, 1);
            }

            if (animationList.length == 0) {
                if (animation) animation.ended = true;
                this.isAnimate = false;
            }
        },
        _resetSameTargetAnimation: function (animation) {
            var animationList = this.animationList;
            for (var i = 0; i < animationList.length; i++) {
                var a = animationList[i];
                if (animation === a) continue;
                if (animation.target === a.target) return true;
            }
            return false;
        },
        removeTarget: function (target) {
            var animationList = this.animationList;
            for (var i = animationList.length - 1; i >= 0; i--) {
                if (animationList[i].target === target) animationList.splice(i, 1);
            }
        },
        createElapseObj: function (aObj, elapsed) {
            if (elapsed < aObj.delay) return null;
            var durationElapsed = elapsed - aObj.delay;
            var animationFn = this.createAnimationFn(aObj);
            canvasTool.update(aObj.animationObj, durationElapsed,  aObj.duration, animationFn);
            return true;
        },
        createAnimationFn: function (obj) {
            switch (obj.animationType) {
                case "ease": return Animation.ease;
                case "ease-in": return Animation.easeIn;
                case "ease-out": return Animation.easeOut;
                case "ease-in-out": return Animation.easeInOut;
            }
            return Animation.ease;
        },
        computeAnimationTotal: function (obj) {
            return obj.duration + obj.delay * obj.time;
        },
        removeAnimationByTarget: function (target) {
            var animationList = this.animationList;
            for (var i = animationList.length - 1; i >= 0; i--) {
                if (animationList[i].component === target) animationList.splice(i, 1);
            }
        },
        add: function (obj) {
            if (!this.isHas(obj.id)) this.animations.push(obj);
        },
        _addPropertyAnimation: function (str, target) {
            var animation = this.parent._createAnimation(str);
            if (!animation) return;
            var name = animation.name;
            if (this.isHas(name)) {
                var obj = {
                    target: target,
                    animation: animation,
                    isAnimate: true
                };
                if (this.animationObj[name]) {
                    this.animationObj[name].push(obj);
                }
                else {
                    this.animationObj[name] = [obj];
                }
            }

            if (!this.isAnimate) {
                this.addProperty({type: "instance", target: this});
            }
        },
        removePropertyAnimation: function (str, target) {
            var animation = this.parent._createAnimation(str);
            if (!animation) return;
            var animationList = this.animationObj[animation.name];
            if (!animationList) return;
            for (var i = 0; i < animationList.length; i++) {
                if (animationList[i].target === target) {
                    animationList.splice(i, 1);
                    break;
                }
            }
        },
    }

    App.define("helpAnimation", {
        animation: HelpAnimation
    })
})